package com.killer.edu.message.server;

import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.corundumstudio.socketio.Configuration;
import com.corundumstudio.socketio.SocketConfig;
import com.corundumstudio.socketio.SocketIOClient;
import com.corundumstudio.socketio.SocketIOServer;
import com.corundumstudio.socketio.listener.ConnectListener;
import com.corundumstudio.socketio.listener.DisconnectListener;
import com.corundumstudio.socketio.listener.ExceptionListener;
import com.corundumstudio.socketio.namespace.Namespace;
import com.corundumstudio.socketio.protocol.Packet;
import com.corundumstudio.socketio.protocol.PacketType;
import com.corundumstudio.socketio.store.RedissonStoreFactory;
import com.killer.edu.common.util.GsonUtil;
import com.killer.edu.common.util.JwtUtil;
import com.killer.edu.message.constant.Constants;
import com.killer.edu.message.entity.Message;

import com.killer.edu.message.server.store.StoreFacotryProvider;
import com.killer.edu.message.util.PushUtils;
import com.killer.edu.message.util.ServerConfigUtils;
import io.netty.channel.ChannelHandlerContext;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import java.util.*;

/**
 * @author ：Killer
 * @date ：Created in 21-5-28 下午4:55
 * @description：${description}
 * @modified By：
 * @version: version
 */
@Slf4j
public class PushServer  {

    public static final PushServer pushServer = new PushServer();
    private Namespace pushNamespace;
    private SocketIOServer server;

    private PushServer() {
        final Configuration configuration=new Configuration();
        configuration.setStoreFactory(StoreFacotryProvider.getRedissonStoreFactory());
        configuration.setAuthorizationListener(new UserAuthorizationListener());
        configuration.setPort(ServerConfigUtils.instance.getWebSocketPort());
        configuration.setContext(ServerConfigUtils.instance.getWebSocketContext());
        //configuration.setOrigin(ServerConfigUtils.instance.getWebSocketOrigin());
        final SocketConfig socketConfig = new SocketConfig();
        //可重用地址，防止处于TIME_WAIT的socket影响服务启动
        socketConfig.setReuseAddress(true);
        configuration.setSocketConfig(socketConfig);
        server = new SocketIOServer(configuration);
        //设置namespace
        pushNamespace=(Namespace)server.addNamespace(ServerConfigUtils.instance.getWebSocketContext());
        configuration.setExceptionListener(new ExceptionListener() {
            @Override
            public void onEventException(Exception e, List<Object> args, SocketIOClient client) {
                UUID sessionId=client.getSessionId();
                log.error("onEventException,sessionId:{},roomList:{}",sessionId,client.get(Constants.ROOM),e);
            }

            @Override
            public void onDisconnectException(Exception e, SocketIOClient client) {
                UUID sessionId=client.getSessionId();
                log.error("onDisconnectException,sessionId:{},roomList:{}",sessionId,client.get(Constants.ROOM),e);
            }

            @Override
            public void onConnectException(Exception e, SocketIOClient client) {
                UUID sessionId=client.getSessionId();
                log.error("onConnectException,sessionId:{},roomList:{}",sessionId,client.get(Constants.ROOM),e);
            }

            @Override
            public void onPingException(Exception e, SocketIOClient client) {
                UUID sessionId=client.getSessionId();
                log.error("onPingException,sessionId:{},roomList:{}",sessionId,client.get(Constants.ROOM),e);
            }

            @Override
            public boolean exceptionCaught(ChannelHandlerContext ctx, Throwable e) throws Exception {
                log.error("exceptionCaught",e);
                return false;
            }
        });
        pushNamespace.addConnectListener(new ConnectListener() {
            @Override
            public void onConnect(SocketIOClient client) {
                //返回由UUID生成的唯一标识
                UUID sid=client.getSessionId();
                JwtUtil.JwtResult user = UserAuthorizationListener.getUserInfo(client.getHandshakeData());
                //自定义了房间id替换默认的
                if (user != null) {
                    //返回当前客户端所在的room名称列表
                    Set<String> rooms = client.getAllRooms();
                    System.out.println("room 1:"+rooms);
                    for (String room : rooms) {
                        //从指定房间离开
                        client.leaveRoom(room);
                    }
                    System.out.println("room 2:"+client.getAllRooms());
                    String userId = Integer.toString(user.getUserId());
                    List<String> roomList = PushUtils.getRoomList(userId, null, null);
                    for (String roomStr : roomList) {
                        // 加入到指定房间
                        client.joinRoom(roomStr);
                        log.info("userId:{} sid:{} join room{}", userId, sid, roomStr);
                        client.set(Constants.ROOM, roomList);

                    }
                    System.out.println("room 3:"+client.getAllRooms());
                }
            }
        });
        pushNamespace.addDisconnectListener(new DisconnectListener() {
            @Override
            public void onDisconnect(SocketIOClient client) {
                UUID sid=client.getSessionId();
                JwtUtil.JwtResult user = UserAuthorizationListener.getUserInfo(client.getHandshakeData());
                if (user != null) {
                    log.info("disconnect userId:{} userName:{} sid:{}", user.getUserId(), user.getUserName(), sid);
                }else{
                    client.disconnect();
                    log.info("client get handShakeData is null,disconnect,sid:{}", sid);
                }
                String userId = Integer.toString(user.getUserId());
                List<String> roomList = PushUtils.getRoomList(userId, null, null);
                for (String roomStr : roomList) {
                    client.leaveRoom(roomStr);
                }
                try {
                    //离开房间
                    List<String> oldRoomList = client.get(Constants.ROOM);
                    System.out.println("oldRoom:"+oldRoomList);
                    if (CollectionUtils.isNotEmpty(oldRoomList)) {
                        for (String room : oldRoomList) {
                            if (StringUtils.isBlank(room)) {
                                continue;
                            }
                            client.leaveRoom(room);
                        }
                    }
                    client.disconnect();
                } catch (Exception e) {
                    log.error("",e);
                }
            }
        });
    }

    /**
     * 推送消息
     *
     * @param message
     */
    public void push(com.killer.edu.message.api.dto.Message message) {
        //获取userId
        Integer userId = message.getUserId();
        //获取房间
        String room = PushUtils.getRoom(null, userId, null);
        //组装消息
        Packet packet = new Packet(PacketType.MESSAGE);
        packet.setSubType(PacketType.EVENT);
        packet.setName("message");
        packet.setNsp(pushNamespace.getName());
        List<Object> objects=new ArrayList<>();
        objects.add(GsonUtil.toJson(message));
        packet.setData(objects);
        Iterable<SocketIOClient> roomClients = pushNamespace.getRoomClients(room);

        System.out.println("roomClients:"+roomClients);
        for (SocketIOClient roomClient : roomClients) {
            roomClient.send(packet);
        }
    }

    /**
     * 同步启动服务；
     */
    public void start() {
        try {
            server.start();
        } catch (Exception e) {
            log.error("Push server start failed!", e);
            System.exit(-1);
        }
    }

    /**
     * 停止服务
     */
    public void stop() {
        server.stop();
    }

    public Map<String, Object> getStatus() {
        HashMap<String, Object> status = new HashMap<>();
        status.put("namespace", pushNamespace.getName());   // namespace
        status.put("rooms", pushNamespace.getRooms());
        status.put("clients", pushNamespace.getAllClients().size());
        return status;
    }
}
